#pragma once
/*include guards*/
#ifndef CONFIG_H
#define CONFIG_H

#include <fstream>
#include <map>
#include <stdexcept>
#include <sstream>
#include <iostream>
using namespace std;
class Data
{
public:
    Data(const string& data = "") : myData(data) {}

    operator int() const
    {
        stringstream ss(myData);
        int val = 0;
        ss >> val;

        if (ss.fail())
        {
            throw runtime_error("Cannot convert to int.");
        }
        return val;
    }

    operator const char*() const
    {
        return myData.c_str();
    }

    operator string() const
    {
        return myData;
    }

    operator bool() const
    {
        return myData == "true" || myData == "True" || myData == "TRUE";
    }

private:
    string myData;
};

class Config
{
public:
    Config(const char* filename, const char fieldSeparator = ':')
    {
        using namespace std;

        fstream file(filename, ios::in);

        if (file)
        {
            string line;

            while (getline(file, line))
            {
                size_t sep = line.find_first_of(fieldSeparator);

                if (sep != string::npos)
                {
                    string key   = trim(line.substr(0, sep));
                    string value = trim(line.substr(sep + 1));

                    if (!key.empty() && !value.empty())
                    {
                        myConfigItems[key] = Data(value);
                    }
                    else
                    {
                        throw runtime_error("Error within configuration file.");
                    }
                }
                else
                {
                    throw runtime_error("Error within configuration file.");
                }
            }
        }
        else
        {
            throw runtime_error("Cannot open config file.");
        }
    }

    Data get(const string& key) const
    {
        map<string, Data>::const_iterator it = myConfigItems.find(key);

        if (it != myConfigItems.end())
        {
            return it->second;
        }
        else
        {
            throw std::runtime_error("Cannot find config item.");
        }
    }

    friend ostream& operator<<(ostream& os, const Config& config)
    {
        for (map<string, Data>::const_iterator it = config.myConfigItems.begin();
                it != config.myConfigItems.end();
                ++it)
        {
            const string& key = it->first;
            const string  val = it->second;

            cout << key << "\t" << val << std::endl;
        }

        return os;
    }

private:
    string trim(string str)
    {
        size_t pos = str.find_first_not_of(" \t\n");

        if (pos != string::npos)
        {
            str.erase(0, pos);
        }

        pos = str.find_last_not_of(" \t\n");

        if (pos != string::npos)
        {
            str.erase(pos + 1);
        }

        return str;
    }

    map<string, Data> myConfigItems;
};
#endif /* end of include guard: CONFIG_H*/

